// Define FORCEDSHOE to force card sequences in the shoe.
// Note: this symbol has to be undefined for the production build.

#define MAX_NUM_SKIP 99

#ifndef FORCEDSHOE 

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//     Filename: Shue.cpp                                                   //                                        //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//  Description: This file implements the Shue class, which encapulates     //
//                1 or more decks of cards to be used in our games.         //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2004 - Realtime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////
#include <string.h>

#include "lib.h"

#include "shue.h"
#include "cardmgr.h"
#include "getrand.h"

//////////////////////////////////////////////////////////////////////////////
/// Constructor
///
/// @param Mgr          Pointer to card manager for the game. Basically
///                       implements the 52 card deck.
///
/// @param numDecks     Number of decks in the shue, 0 means Infinite.
///
//////////////////////////////////////////////////////////////////////////////
Shue::Shue(CardMgr* pMgr, int32 numDecks, const ShueGameInfo &gameInfo) : m_pMgr(pMgr),
																m_GameInfo(gameInfo),
																m_nSeed((uint32)-1),
																m_oR250(m_nSeed)
{
    // 0 Decks passed in tells us to create an infinite shue.
    // Also 255 is a special token that means infinite as well.  This is a bit
    //  of a hack, but for right now we do it this way because if we store 0
    //  in the database, it means take the default value.
    if ( (numDecks == 0) || (numDecks == 255) )
    {
        m_bInfinite = true;
        m_nCount = 0;
        m_nNext = 0;
	    m_pDeck = NULL;
    }
    else
    {
        m_bInfinite = false;
        m_nCount = numDecks * m_pMgr->getDeckSize();
        m_nNext = 0;
	    m_pDeck = new uint8[m_nCount];
    }

    // Initialize
    fresh();
}

//////////////////////////////////////////////////////////////////////////////
/// Destructor
//////////////////////////////////////////////////////////////////////////////
Shue::~Shue()
{
    if ( m_pDeck )
    {
    	delete [] m_pDeck;
    	m_pDeck = (uint8*)0;
    }

	m_nCount = m_nNext = 0;
	m_pMgr = (CardMgr*)0;
}

//////////////////////////////////////////////////////////////////////////////
/// getNumDecks
//////////////////////////////////////////////////////////////////////////////
int32
Shue::getNumDecks()
{
    int32 nRetVal = 0;

	ASSERT(m_pMgr != (CardMgr*)0);

    if ( !m_bInfinite )
    {
        nRetVal = m_nCount / m_pMgr->getDeckSize();
    }

	return nRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// getNumCards
//////////////////////////////////////////////////////////////////////////////
int32
Shue::getNumCards()
{
    // NOTE: This function really doesn't make sense for an infinite shue
    //        since there is no way to express infinity here.
    ASSERT( !m_bInfinite );

	return m_nCount;
}


//////////////////////////////////////////////////////////////////////////////
// getNumCardsLeft
//////////////////////////////////////////////////////////////////////////////
int32
Shue::getNumCardsLeft()
{
    // NOTE: This function really doesn't make sense for an infinite shue
    //        since there is no way to express infinity here.
    ASSERT( !m_bInfinite );

    return m_nCount-m_nNext;
}

//////////////////////////////////////////////////////////////////////////////
/// getCard
//////////////////////////////////////////////////////////////////////////////
uint8
Shue::getCard()
{
    uint8 nCard = 0;
    
    // For infinite shue, just pick a random card
    if ( m_bInfinite )
    {
        uint32 n = m_oR250.rani() % m_pMgr->getDeckSize();
        nCard = m_pMgr->makeCard(n);
    }
    else
    {
        // Make sure there are still some cards left in the Shue
        if ( m_nNext < m_nCount )
        {
            // Pick the next card in the shue and update the next
            //  index
            nCard = m_pDeck[m_nNext];
            m_nNext++;
        }
        else
        {
            DEBUGMSG(("No cards left in shue, reshuffling!"));

            // If no cards were left, re-shuffle and try again.
            shuffle();
            nCard = getCard();
        }
    }
    
    return nCard;
}

//////////////////////////////////////////////////////////////////////////////
/// lookAtCard
//////////////////////////////////////////////////////////////////////////////
uint8
Shue::lookAtCard(int32 index)
{
    // For an infinite shue, we would have to generate all cards up to index
    //  and save them for later.  We're not taking that approach right now,
    //  so this function does not work for infinite shue at this time.
    ASSERT( !m_bInfinite );

	return (uint8)(index < m_nCount ? m_pDeck[index] : 0);
}

//////////////////////////////////////////////////////////////////////////////
/// pushBackNCards
//////////////////////////////////////////////////////////////////////////////
bool8
Shue::pushBackNCards(int32 n)
{
    // As above, this function is not implemented for infinite shue at this 
    //   time.
    ASSERT( !m_bInfinite );

    if (m_nNext >= n)
	{
		m_nNext -= n;
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////////
/// shuffle
//////////////////////////////////////////////////////////////////////////////
void
Shue::shuffle()
{
	shuffle(getrand());
}

//////////////////////////////////////////////////////////////////////////////
/// shuffle
//////////////////////////////////////////////////////////////////////////////
void
Shue::shuffle(uint32 Seed)
{
	int32 i, remain;
	uint32 lucky;

    // Store seed
    m_nSeed = Seed;

    // Set the seed in our random number generator
    m_oR250.seed( Seed );

    // For infinite shue, do nothing.
    if ( m_bInfinite )
    {
        return;
    }

	if ( !m_pDeck )
	{
		m_nNext = m_nCount = 0;
		return;
	}

    // Loop through each card and swap positions with a random card, thus
    //  shuffling the entire Shue
	for (i = 0; i < m_nCount; i++)
	{
		remain = m_nCount - i;
        ////////////////////////////////////////////////
        //BACKGROUND CYCLING IMPROVEMENT SUGGESTED BY TST
        //DATE: 07-04-2010 
        //Author:RPF
        ////////////////////////////////////////////////
        //int nskip=rand()%MAX_NUM_SKIP;
        //We can't use the rand() function because the restore game from a previous saved game builds the shue
        //upon the seed stored in the database. (CSN-2704)
        int nskip=m_oR250.rani()%MAX_NUM_SKIP;
        for (int t=0;t<nskip;t++)
           m_oR250.rani();

		lucky = m_oR250.rani() % remain;

		swap(m_pDeck[lucky], m_pDeck[remain - 1]);
	}

    // Reset starting point to deal from
	m_nNext = 0;
}


//////////////////////////////////////////////////////////////////////////////
/// save
//////////////////////////////////////////////////////////////////////////////
void
Shue::save(ShueSave* dest)
{
	dest->next = m_nNext;
	dest->seed = m_nSeed;
}

//////////////////////////////////////////////////////////////////////////////
/// restore
//////////////////////////////////////////////////////////////////////////////
void
Shue::restore(ShueSave* src)
{
	fresh();
	shuffle(src->seed);

	if (m_pDeck)
    {
		m_nNext = src->next;
    }
}

//////////////////////////////////////////////////////////////////////////////
/// fresh
///
/// Initializes all decks in the shue
//////////////////////////////////////////////////////////////////////////////
void
Shue::fresh()
{
	if (m_pDeck)
	{
		int32 i, j, numDecks;

		numDecks = getNumDecks();

		m_nNext = 0;
		for (i = 0; i < numDecks; i++)
        {
			for (j = 0; j < m_pMgr->getDeckSize(); j++)
            {
				m_pDeck[m_nNext++] = m_pMgr->makeCard(j);
            }
        }

		m_nNext = 0;
	}
	else
    {
		m_nNext = m_nCount = 0;
    }
}

//////////////////////////////////////////////////////////////////////////////
/// swap
///
/// Internal helper to make the shuffle function easier to read,
//////////////////////////////////////////////////////////////////////////////
void 
Shue::swap( uint8& a, uint8& b )
{
    uint8 temp;

    temp = a;
	a = b;
	b = temp;
}

#else // FORCEDSHOE defined

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//     Filename: Shue.cpp ( FORCED HANDS DEBUG VERSION )                    //
//                                                                          //
//      WARNING DO NOT COMPILE INTO MAIN APPLICATION !!!!!                  //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//  Description: This file implements the Shue class, which encapulates     //
//                1 or more decks of cards to be used in our games.         //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2006 - Realtime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////

#include <string.h>

#include "lib.h"

#include "shue.h"
#include "cardmgr.h"
#include "getrand.h"

#include "opts.h"
#include "odbc.h"

#pragma message ("****** WARNING *******************************************")
#pragma message ("         YOU ARE COMPILING DEBUG TEST SHUE!!!!            ")
#pragma message ("      Please ensure you do not add this to release code!!!")
#pragma message ("**********************************************************")


ShueGameInfo::ShueGameInfo()
{
	// CSN-13015 - Shue minimum size
	shueMinSize = 1;
}

//////////////////////////////////////////////////////////////////////////////
/// Constructor
///
/// @param Mgr          Pointer to card manager for the game. Basically
///                       implements the 52 card deck.
///
/// @param numDecks     Number of decks in the shue, 0 means Infinite.
///
//////////////////////////////////////////////////////////////////////////////
Shue::Shue(CardMgr* pMgr, int32 numDecks, const ShueGameInfo &gameInfo) : m_pMgr(pMgr),
m_GameInfo(gameInfo),
m_nSeed((uint32)-1),
m_oR250(m_nSeed),
m_bInfinite(false),
m_nCount(0),
m_nNext(0),
m_pDeck(0),
m_pDBPool(0),
m_pDBConn(0),
m_nNumDecks(numDecks),
m_bDebugShueLoaded(false)
{
	// For the forced shue, we are going to read in all the cards from the database
	DEBUGMSG( (" ****** NEW DEBUG SHUE CREATED ******* ") );
    //20090505 RPF Don't check the casino name anylonger
    //only pay attention on the allowTesting flag
	//if( ServerOpts::bAllowTesting && ServerOpts::IsDebugCasinoName() )
    if( ServerOpts::bAllowTesting)
	{
		// Create new db pool
		m_pDBPool = new ODBCPool();
		if ( m_pDBPool )
		{
			// Initialize the database pool
			if (!m_pDBPool->init())
			{
				delete m_pDBPool;
				m_pDBPool = (ODBCPool*)0;
			}

			// Now create a connection for us to use
			m_pDBConn = m_pDBPool->open(ServerOpts::dbdsn, ServerOpts::dblogin, ServerOpts::dbpasswd);
			if ( !m_pDBConn )
			{
				delete m_pDBPool;
				m_pDBPool = (ODBCPool*)0;
			}
			else
			{
				// Ok, now load the cards from the database
				ODBCQuery* pQuery = m_pDBConn->query();
				if ( pQuery )
				{
					String sQuery;
					sQuery.sprintf("SELECT cards FROM ForcedShue WHERE gameId = %d AND machId = %d AND PID = '%S'",
					               m_GameInfo.gameId, m_GameInfo.machId, (const wchar_t*)m_GameInfo.pid);
					pQuery->define(sQuery);

					// String that holds all the cards from the DB
					String sAllCards;

					if ( pQuery->exec() )
					{
						// Loop through all the rows in the database (should be only 1 row per game returned)
						while( pQuery->getRow() )
						{
							m_bDebugShueLoaded = true;

							String sCards;
							pQuery->getData(1, sCards);

							// Add to all cards
							sAllCards += sCards;
						}

						if (m_bDebugShueLoaded)
						{
							// Now, create the deck based on the cards read in
							m_nCount = sAllCards.getLength() / 2;  /* 2 chars per card */

							// CSN-13015 - Check forced shue size
							if (m_nCount >= m_GameInfo.shueMinSize)
							{
								m_pDeck = new uint8[m_nCount];

								// Now, build the actual deck
								uint8 n = 0;
								for (uint8 i = 0; i < m_nCount; i++)
								{
									// Add that card to the deck
									m_pDeck[i] = encodeCard(sAllCards[n], sAllCards[n+1]);

									// kharmon.  09-08-05.  #6297.  Some extra code to handle bad data in
									// the database.  Skip this card if it is bad.
									if (m_pDeck[i] == 0xFF)
									{
										--i;         // back up one to put the next card in this slot.
										--m_nCount;  // reduce the number of cards in the deck by 1.
									}

									// Increment Index into our string
									n += 2;   
								}
							}
							else
							{
								// CSN-13015 - Debug shue doesn't have enough number of cards
								m_bDebugShueLoaded = false;
								DEBUGMSG(("Omitting debug shue, only %d cards retrieved and %d were required",
								         m_nCount, m_GameInfo.shueMinSize));								
							}
						}
					}

					// Done with the query
					delete pQuery;
				}
			}
		}
	}
	//if no debug cards were found for this game/machine then just load up a regular
	//random shue
	if(! m_bDebugShueLoaded)
	{
		if ( (numDecks == 0) || (numDecks == 255) )
		{
			m_bInfinite = true;
			m_nCount = 0;
			m_nNext = 0;
			m_pDeck = NULL;
		}
		else
		{
			m_bInfinite = false;
			m_nCount = numDecks * m_pMgr->getDeckSize();
			m_nNext = 0;
			m_pDeck = new uint8[m_nCount];
		}

		// Initialize
		fresh();
		DEBUGMSG(("Random shue loaded (%d cards)", m_nCount));
	}
	else
	{
		DEBUGMSG(("Forced shue loaded (%d cards)", m_nCount));
	}
}

//////////////////////////////////////////////////////////////////////////////
/// Destructor
//////////////////////////////////////////////////////////////////////////////
Shue::~Shue()
{
	if ( m_pDeck )
	{
		delete [] m_pDeck;
		m_pDeck = (uint8*)0;
	}

	m_nCount = m_nNext = 0;
	m_pMgr = (CardMgr*)0;

	if ( m_pDBConn )
	{
		delete m_pDBConn;
	}

	if ( m_pDBPool )
	{
		delete m_pDBPool;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// getNumDecks
//////////////////////////////////////////////////////////////////////////////
int32
Shue::getNumDecks()
{
	if(m_bDebugShueLoaded)
		return m_nNumDecks;

	else
	{
		int32 nRetVal = 0;

		ASSERT(m_pMgr != (CardMgr*)0);

		if ( !m_bInfinite )
		{
			nRetVal = m_nCount / m_pMgr->getDeckSize();
		}

		return nRetVal;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// getNumCards
//////////////////////////////////////////////////////////////////////////////
int32
Shue::getNumCards()
{
	// NOTE: This function really doesn't make sense for an infinite shue
	//        since there is no way to express infinity here.
	ASSERT( !m_bInfinite );

	return m_nCount;
}


//////////////////////////////////////////////////////////////////////////////
// getNumCardsLeft
//////////////////////////////////////////////////////////////////////////////
int32
Shue::getNumCardsLeft()
{
	// NOTE: This function really doesn't make sense for an infinite shue
	//        since there is no way to express infinity here.
	ASSERT( !m_bInfinite );

	return m_nCount-m_nNext;
}

//////////////////////////////////////////////////////////////////////////////
/// getCard
//////////////////////////////////////////////////////////////////////////////
uint8
Shue::getCard()
{
	uint8 nCard = 0;

	if ( m_nCount > 0 )
	{
		if ( m_nNext >= m_nCount )
		{
			m_nNext = 0;
		}

		// Pick the next card in the shue and update the next
		//  index
		nCard = m_pDeck[m_nNext];
		m_nNext++;
	}

	DEBUGMSG( ("getCard called... count = %d, next = %d : returning %x", m_nCount, m_nNext, nCard) );

	return nCard;
}

//////////////////////////////////////////////////////////////////////////////
/// lookAtCard
//////////////////////////////////////////////////////////////////////////////
uint8
Shue::lookAtCard(int32 index)
{
	// For an infinite shue, we would have to generate all cards up to index
	//  and save them for later.  We're not taking that approach right now,
	//  so this function does not work for infinite shue at this time.
	ASSERT( !m_bInfinite );

	return (uint8)(index < m_nCount ? m_pDeck[index] : 0);
}

//////////////////////////////////////////////////////////////////////////////
/// pushBackNCards
//////////////////////////////////////////////////////////////////////////////
bool8
Shue::pushBackNCards(int32 n)
{
	// As above, this function is not implemented for infinite shue at this 
	//   time.
	ASSERT( !m_bInfinite );

	if (m_nNext >= n)
	{
		m_nNext -= n;
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////////
/// shuffle
//////////////////////////////////////////////////////////////////////////////
void
Shue::shuffle()
{
	if(! m_bDebugShueLoaded)
		shuffle(getrand());
}

//////////////////////////////////////////////////////////////////////////////
/// shuffle
//////////////////////////////////////////////////////////////////////////////
void
Shue::shuffle(uint32 Seed)
{
	if(! m_bDebugShueLoaded)
	{
		int32 i, remain;
		uint32 lucky;

		// Store seed
		m_nSeed = Seed;

		// Set the seed in our random number generator
		m_oR250.seed( Seed );

		// For infinite shue, do nothing.
		if ( m_bInfinite )
		{
			return;
		}

		if ( !m_pDeck )
		{
			m_nNext = m_nCount = 0;
			return;
		}

		// Loop through each card and swap positions with a random card, thus
		//  shuffling the entire Shue
		for (i = 0; i < m_nCount; i++)
		{
			remain = m_nCount - i;
            ////////////////////////////////////////////////
            //BACKGROUND CYCLING IMPROVEMENT SUGGESTED BY TST
            //DATE: 07-04-2010 
            //Author:RPF
            ////////////////////////////////////////////////
            int nskip=m_oR250.rani()%MAX_NUM_SKIP;
            for (int t=0;t<nskip;t++)
               m_oR250.rani();

			lucky = m_oR250.rani() % remain;

			swap(m_pDeck[lucky], m_pDeck[remain - 1]);
		}

		// Reset starting point to deal from
		m_nNext = 0;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// save
//////////////////////////////////////////////////////////////////////////////
void
Shue::save(ShueSave* dest)
{
	dest->next = m_nNext;
	dest->seed = m_nSeed;
}

//////////////////////////////////////////////////////////////////////////////
/// restore
//////////////////////////////////////////////////////////////////////////////
void
Shue::restore(ShueSave* src)
{
	if(! m_bDebugShueLoaded)
	{
		fresh();
		shuffle(src->seed);

		if (m_pDeck)
		{
			m_nNext = src->next;
		}
	}
}

//////////////////////////////////////////////////////////////////////////////
/// fresh
///
/// Initializes all decks in the shue
//////////////////////////////////////////////////////////////////////////////
void
Shue::fresh()
{
	if (m_pDeck)
	{
		int32 i, j, numDecks;

		numDecks = getNumDecks();

		m_nNext = 0;
		for (i = 0; i < numDecks; i++)
		{
			for (j = 0; j < m_pMgr->getDeckSize(); j++)
			{
				m_pDeck[m_nNext++] = m_pMgr->makeCard(j);
			}
		}

		m_nNext = 0;
	}
	else
	{
		m_nNext = m_nCount = 0;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// swap
///
/// Internal helper to make the shuffle function easier to read,
//////////////////////////////////////////////////////////////////////////////
void 
Shue::swap( uint8& a, uint8& b )
{
	uint8 temp;

	temp = a;
	a = b;
	b = temp;
}

//////////////////////////////////////////////////////////////////////////////
/// encodeCard
///
/// Internal helper used to encode cards read from the database
//////////////////////////////////////////////////////////////////////////////
uint8 Shue::encodeCard( uint16 nRank, uint16 nSuit )
{
	int nOrigRank = nRank;
	int nOrigSuit = nSuit;
	switch (nSuit)
	{
	case 'C': nSuit = 0x10;        break;
	case 'D': nSuit = 0x20;     break;
	case 'H': nSuit = 0x30;        break;
	case 'S': nSuit = 0x40;        break;
	case 'X': nSuit = 0x50;        break;  // kharmon.  09-08-05.  Support for jokers...
	default:  nSuit = 0;        break;
	}
	switch (nRank)
	{
	case 'A': nRank = 14;        break;
	case 'T': nRank = 10;        break;
	case 'J': nRank = 11;        break;
	case 'Q': nRank = 12;        break;
	case 'K': nRank = 13;        break;
	default:  nRank = nRank - '0';    break;
	}

	uint8 nValue = (uint8)((nSuit & 0xF0) | (nRank & 0x0F));

	// kharmon.  09-08-05.  #6297.  Some extra code to handle bad data in
	// the database.
	if(nSuit==0 || nRank > 14 || nRank<1)
	{
		DEBUGMSG( ("INVALID FORCED SHUE CARD:  %c%c", (char) nOrigRank, (char) nOrigSuit) );
		nValue = 0xff;
	}

	return nValue;
}



#endif // FORCEDSHOE
